home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1985-1992 New York University
- *
- * This file is part of the Ada/Ed-C system. See the Ada/Ed README file for
- * warranty (none) and distribution info and also the GNU General Public
- * License for more details.
-
- */
-
- /*
- +---------------------------------------------------+
- | |
- | I N T E R P T A S K I N G |
- | |
- | (C Version) |
- | |
- | Adapted From Low Level SETL version written by |
- | |
- | Monte Zweben |
- | Philippe Kruchten |
- | Jean-Pierre Rosen |
- | |
- | Original High Level SETL version written by |
- | |
- | Robert B. K. Dewar |
- | Clint Goss |
- | Tracey M. Siesser |
- | Bernard D. Banner |
- | Stephen C. Bryant |
- | Gerry Fisher |
- | |
- | C version written by |
- | |
- | Robert B. K. Dewar |
- | |
- +---------------------------------------------------+
- */
-
-
- /* Include standard header modules */
- #include <stdlib.h>
- #include <stdio.h>
- #include "config.h"
- #include "ipar.h"
- #include "ivars.h"
- #include "int.h"
- #include "tasking.h"
- #include "segment.h"
- #include "iparprots.h"
- #include "intaprots.h"
- #include "intbprots.h"
- #include "intcprots.h"
- #include "miscprots.h"
- #include "taskingprots.h"
-
- #ifdef vms
- /*
- #include "adaexec.h"
- */
- #endif
-
- #ifdef IBM_PC
- #include <time.h>
- static void sleep(unsigned);
- #endif
-
- static void done_creation();
- static void create_task(int, int, struct rts_item *, int);
- static void done_activation();
- static void activate_self(int, int);
- static void kill(int);
- static void abortme();
- static void post_abort_one();
- static void post_complete_task_one();
- static void post_complete_block();
- static void post_complete_block_two();
- static void task_term();
- static void free_task_space();
- static void check_termination(int, int);
- static void check_done(int, int);
- static void check_unterm();
- static void tell_termination(int);
- static void uncreate_tasks(int);
- static void my_set_timer(struct io_item_type *, long);
- static int long my_reset_timer(long);
- static void wait(long, int);
- static void post_wait();
- static void catcher(long);
- static struct io_item_type *chain(int, long);
- static void check_free(struct io_item_type *);
- static int get_io(struct io_item_type *);
- static void disable_io(struct io_item_type **);
- static int remove_io(struct io_item_type **);
- static void schedule(int);
- static void finish_action(struct rts_item *);
- static void make_ready(int, int);
- static void transfer(int);
- static void evaluate_guards(int [], int);
- static void close_guards(int [], int);
- static void post_selective_wait(int, int, int [], int []);
- static void accept_rdv(int, int, int);
- static void post_entry_call();
- static void einit(struct entry_type *);
- static int eremove(int);
- static void eput(struct entry_type *, int, struct q_item **);
- static int eget(struct entry_type *);
- static void add_lock(struct lock_type *);
- static void add_unlock(struct lock_type *);
- static void del_lock(struct lock_type *);
- static void del_unlock(struct lock_type *);
- static int entry_number(int, int, int);
- static void multisignal(int, int);
- static void add_serviced(int, int);
- static int remove_serviced();
- static void qinit();
- static void enqueue(int, int);
- static void enqueue_item(int, struct rts_item *);
- static struct rts_item *dequeue(struct ready_q *, int *);
- static void context_switch();
-
- /* Global variables for tasking management */
-
- /* id of the owner of the corr. stack */
- static int original_task[MAX_TASKS];
- /* Head of task chained waiting on clock */
- static struct io_item_type *clock_head;
- /* Highest priority of tasks to schedule */
- static int highest_priority;
- /* Previous value of time */
- static long last_time;
- /* temporary decl of ready queues */
- static struct ready_q *ready_queue[MAX_PRIO];
-
- /*-------------------------------------------------------------------------*
- * ---- T A S K I N G S Y S T E M --- *
- * *
- * The module contains all of the tasking control routines. It is *
- * divided into nine major sections. Itemized under each section are the *
- * routines which are callable from outside the tasking module. Unless *
- * interface changes are made it is essential that the status of the *
- * stack, on entry and exit from these routines, be maintained. *
- * *
- * - Task Initialization *
- * - Task Creation *
- * * start_creation *
- * - Task Activation *
- * * start_activation *
- * * end_activation *
- * - Task Abortion *
- * * abort *
- * - Task Termination *
- * * complete_task *
- * * complete_block *
- * * terminate_unactivated *
- * * purge_rdv *
- * - Timer Maintainence *
- * * delay_stmt *
- * - Task Scheduler *
- * - Rendezvous *
- * * entry_call *
- * * selectvie_wait *
- * * end_rendezvous *
- * - Utility Routines (queue routines etc. *
- * * raise_in_caller *
- * * is_callable *
- * * is_terminated *
- * * count *
- * *
- *-------------------------------------------------------------------------*/
-
-
- /*-------------------------------------------------------------------------*/
- /* T A S K I N G I N I T I A L I Z A T I O N */
- /* */
- /* Procedure to perform necessary initialization for tasking system */
- /*-------------------------------------------------------------------------*/
-
- void initialize_tasking() /*;initialize_tasking*/
- {
- int i;
- int *p;
- long itime();
-
- /* Initialize variables */
-
- last_task = 1;
-
- for (i = 0; i < MAX_TASKS; i++) {
- STACK(i) = (int *)0;
- ORIG(i) = NULL_TASK;
- }
- qinit();
- time_offset = 0;
- last_time = itime();
- clock_head = NULL;
- next_clock = itime();
- next_clock_flag = FALSE;
-
- /* Initiate the idle task */
-
- p = STACK(0) = (int *) malloc((unsigned) sizeof(int) * main_task_size);
- ORIG(0) = 0;
- if (p == (int *)0) {
- #ifdef vms
- LIB$STOP(MSG_NOSTACK);
- #else
- printf("Unable to allocate stack\n");
- exit(RC_ABORT);
- #endif
- }
-
- TCB_ABNORMAL(0) = 0;
- TCB_ACTION(0) = NO_ACTION;
- TCB_BROTHER(0) = 0;
- TCB_BLOCK_PTR(0) = (int *) 0;
- TCB_CURR_ENTRY(0) = NULL;
- TCB_EVENT(0) = NO_EVENT;
- TCB_EXCEPTION(0) = 0;
- TCB_ENTRY_ITEM(0) = 0;
- TCB_FIRST(0) = 0;
- TCB_ID(0) = 0;
- TCB_IO_ITEM(0) = NULL;
- TCB_MASTER_TASK(0) = NULL_TASK;
- TCB_MASTER_BLOCK(0) = 0;
- TCB_NEXT(0) = NULL_TASK;
- TCB_NUM_ITEMS(0) = 0;
- TCB_NUM_NOTERM(0) = 0;
- TCB_NUM_DEPS(0) = 0;
- TCB_NUM_ENTRIES(0) = 0;
- TCB_NUM_EVENTS(0) = 0;
- TCB_PARENT(0) = NULL_TASK;
- TCB_PRIO(0) = 0;
- TCB_RDV(0) = 0;
- TCB_RTS_ITEM(0) = NULL;
- TCB_SAVE_PRIO(0) = 0;
- TCB_SERVICED(0) = 0;
- TCB_STATUS(0) = ACTIVE;
- TCB_TBASE(0) = 1;
- TCB_TOFF(0) = 52;
- TCB_WHAT(0) = 0;
- TCB_WHO(0) = NULL_TASK;
-
- /* Set context for idle task */
-
- STACKPTR(0) = WORDS_TCB;
- tp = 0;
- cur_stack = STACK(tp);
- cur_stackptr = STACKPTR(tp);
- bfp = 0;
- ip = TASK_CODE_OFFSET;
- cs = 1;
- sfp = 0;
- lin = 0;
- exr = 0;
- cur_code = code_segments[cs];
- }
-
-
- /*--------------------------------------------------------------------------*/
- /* T A S K C R E A T I O N */
- /* */
- /* The following set of routines perform task creation */
- /*--------------------------------------------------------------------------*/
-
- /*-------------------------------------------------------------------------*/
- /* START CREATION */
- /* Create an array of tasks, do so by creating a RTS item for the array */
- /* As tasks become ready to run they will perform activations etc. first */
- /* using this RTS item */
- /*-------------------------------------------------------------------------*/
-
- void start_creation(int templ_base, int templ_off) /*;start_creation*/
- {
- int mult = 1;
- struct rts_item *rts;
-
- if (BLOCK_FRAME->bf_tasks_declared == NULL)
- push_task_frame(NULL_TASK); /* create null frame */
-
- rts = (struct rts_item *) malloc(sizeof(struct rts_item));
- if (rts == (struct rts_item *)0) {
- raise(STORAGE_ERROR, "Allocating space for task");
- return;
- }
- rts->tcbs = (int *) malloc((unsigned) sizeof(int)*mult);
- if (rts->tcbs == (int *)0) {
- raise(STORAGE_ERROR, "Allocating space for task");
- return;
- }
- RTS_TYPE(rts) = CREATE;
- RTS_PRIO(rts) = MY_PRIO;
- RTS_TEMPL_BASE(rts) = templ_base;
- RTS_TEMPL_OFF(rts) = templ_off;
- RTS_MULT(rts) = mult;
- RTS_NEXT(rts) = NULL;
- RTS_PARENT(rts) = tp;
-
- MY_WHAT = NULL_TASK;
- MY_NUM_ITEMS = mult;
- enqueue_item(MY_PRIO, rts);
- schedule(DONE_CREATION);
- }
-
- /*--------------------------------------------------------------------------*/
- /* DONE CREATION */
- /* Performed after all of my tasks have finished creation. Add current */
- /* RTS item to chain */
- /*--------------------------------------------------------------------------*/
-
- static void done_creation() /*;done_creation*/
- {
- if (MY_EXCEPTION == STORAGE_ERROR) {
- raise(STORAGE_ERROR, "Not enough space for new tasks");
- MY_EXCEPTION = 0;
- }
- if (MY_WHAT != NULL_TASK) /* MY_WHAT is leader of RTS */
- TCB_BROTHER(MY_WHAT) = FAS(MY_TASKS_DECLARED, MY_WHAT);
- PUSH(MY_WHAT); /* Must add item to chain of rts items */
- }
-
- /*-----------------------------------------------------------------------*/
- /* CREATE TASK */
- /* Procedure to create task and put it on parents frame chain */
- /*-----------------------------------------------------------------------*/
-
- static void create_task(int templ_base, int templ_off, struct rts_item *rts,
- int my_id) /*;create_task*/
- {
- int task_nr, parent; /* nr of the task to be created */
- int i, *p, old_last_task; /* temporary variable */
-
- /* STEP 1: Search the stack_segments for a stack */
-
- old_last_task = last_task;
- task_nr = INC(last_task);
- parent = RTS_PARENT(rts);
- for(;task_nr < old_last_task + MAX_TASKS; task_nr = INC(last_task))
- if (ORIG(task_nr) == NULL_TASK) break;
- if (task_nr >= old_last_task + MAX_TASKS) {/* Error if no avail stack */
- FAS(&(TCB_EXCEPTION(parent)),PROGRAM_ERROR);
- multisignal(parent, NO_EVENT);
- return;
- }
-
- /* STEP 2: Create task frame for new task */
-
- p=STACK(task_nr) = (int *) malloc((unsigned) sizeof(int)*new_task_size);
- ORIG(task_nr) = task_nr;
-
- /* STEP 3 : Create storage area for task and initialize */
- if (p == (int *)0) {
- RTS_TCBS(rts, my_id) = NULL_TASK;
- DEC(RTS_MULT(rts));
- FAS(&(TCB_EXCEPTION(parent)), STORAGE_ERROR);
- STACKPTR(task_nr) = 0;
- multisignal(parent, NO_EVENT);
- }
- else {
- TCB_ABNORMAL(task_nr) = 0;
- TCB_ACTION(task_nr) = ACTIVATE_SELF;
- TCB_BLOCK_PTR(task_nr) = (int *) 0;
- TCB_BROTHER(task_nr) = NULL_BROTHER;
- TCB_CURR_ENTRY(task_nr) = NULL;
- TCB_ENTRY_ITEM(task_nr) = NULL;
- TCB_EVENT(task_nr) = NO_EVENT;
- TCB_EXCEPTION(task_nr) = 0;
- TCB_FIRST(task_nr) = 0;
- TCB_ID(task_nr) = my_id;
- TCB_IO_ITEM(task_nr) = NULL;
- TCB_MASTER_BLOCK(task_nr) = 0;
- TCB_MASTER_TASK(task_nr) = NULL_TASK;
- TCB_NEXT(task_nr) = NULL_TASK;
- TCB_NUM_ITEMS(task_nr) = 0;
- TCB_NUM_DEPS(task_nr) = 0;
- TCB_NUM_ENTRIES(task_nr) =
- TASK(ADDR(templ_base,templ_off))->nb_entries;
- TCB_NUM_EVENTS(task_nr) = 0;
- TCB_NUM_NOTERM(task_nr) = 0;
- TCB_PARENT(task_nr) = RTS_PARENT(rts);
- TCB_PRIO(task_nr) = TASK(ADDR(templ_base, templ_off))->priority;
- TCB_RDV(task_nr) = 0;
- TCB_RTS_ITEM(task_nr) = rts;
- TCB_SAVE_PRIO(task_nr) = 0;
- TCB_SERVICED(task_nr) = NULL_TASK;
- TCB_STATUS(task_nr) = ACTIVATING;
- TCB_TBASE(task_nr) = templ_base;
- TCB_TOFF(task_nr) = templ_off + WORDS_TASK;
- TCB_WHAT(task_nr) = 0;
- TCB_WHO(task_nr) = NULL_TASK;
-
- /* add space for queue headers */
-
- for (i=1; i<=TASK(ADDR(templ_base,templ_off))->nb_entries; i++)
- einit(TCB_ENTRY(task_nr, i));
- p = (int *)(TCB_ENTRY(task_nr, i));
-
- *p++ = 0; /* EXR */
- *p++ = 0; /* LIN */
- *p++ = 0; /* SFP */
- *p++ = 0; /* CS */
- *p++ = 0; /* IP */
- STACKPTR(task_nr) = p - STACK(task_nr);
- *p++ = 0; /* BFP */
- #ifdef TRACE
- if(tasking_trace)
- printf("task %d creating task %d\n",tp,task_nr);
- #endif
- RTS_TCBS(TCB_RTS_ITEM(task_nr), TCB_ID(task_nr)) = task_nr;
- TCB_WHAT(TCB_PARENT(task_nr)) = task_nr;
- multisignal(RTS_PARENT(TCB_RTS_ITEM(task_nr)), NO_EVENT);
- }
- }
-
- /*--------------------------------------------------------------------------*
- * T A S K A C T I V A T I O N *
- * *
- * The following set of routines perform task activation *
- *--------------------------------------------------------------------------*/
-
-
- /*--------------------------------------------------------------------------*
- * START ACTIVATION *
- * This routines is used by a task to preprocess it's task list in order *
- * to find out how many tasks must be activated. It then adds the items *
- * in the tasks list to the ready queue in order that they may then be *
- * activated in parallel. When they are done being activated, the parent *
- * task may continue processing. *
- *--------------------------------------------------------------------------*/
-
- void start_activation(int task_list, int task_master, int task_block)
- /*;start_activation*/
- {
- int i, task, next_task, num_tasks;
- struct rts_item *item;
-
- if (MY_ABNORMAL) {
- uncreate_tasks(task_list);
- abortme();
- return;
- }
-
- task = task_list;
- num_tasks = 0;
- while (task != NULL_TASK) {
- num_tasks += RTS_MULT(TCB_RTS_ITEM(task));
- task = TCB_BROTHER(task);
- }
- MY_NUM_ITEMS = num_tasks;
- MY_EVENT = NO_EVENT;
-
- if (TCB_ABNORMAL(task_master)) return;
-
- MY_STATUS = ACTIVATING;
- task = task_list;
- for (i=0;i<num_tasks;i++) {
- next_task = TCB_BROTHER(task);
- item = TCB_RTS_ITEM(task);
- RTS_MASTER_TASK(item) = task_master;
- RTS_MASTER_BLOCK(item) = task_block;
- RTS_PRIO(item) = MY_PRIO;
- RTS_TYPE(item) = ACTIVATE;
- RTS_NEXT(item) = NULL;
- enqueue_item(MY_PRIO, item);
- task = next_task;
- }
- schedule(DONE_ACTIVATION);
- }
-
- /*--------------------------------------------------------------------------*/
- /* DONE ACTIVATION */
- /* This routines is called by the parent task when all of its tasks have */
- /* finished their activation. It checks that it is ok, if not it aborts */
- /* all of the children. */
- /*--------------------------------------------------------------------------*/
-
- static void done_activation() /*;done_activation*/
- {
- if (MY_ABNORMAL) {
- abortme();
- return;
- }
- if (MY_EVENT == TASKERR_EVENT) {
- raise(TASKING_ERROR, "Tasking error in activation");
- MY_EXCEPTION = 0;
- }
- else if (MY_EVENT == PROGERR_EVENT) {
- raise(PROGRAM_ERROR, "Activating an unelaborated task");
- MY_EXCEPTION = 0;
- }
- MY_ACTION = NO_ACTION;
- }
-
- int union_tasks_declared(int list1, int list2) /*;union_tasks_declared */
- {
- int head2, tail1;
-
- if (list1 == NULL_TASK) return list2;
- if (list2 == NULL_TASK) return list1;
-
- tail1 = list1;
- while (TCB_BROTHER(tail1) != NULL_TASK) tail1 = TCB_BROTHER(tail1);
-
- head2 = FAS(&(list2), list1);
- FAS(&(TCB_BROTHER(tail1)), head2);
- return list1;
- }
-
- /*------------------------------------------------------------------------*/
- /* TASK ACTIVATION */
- /* Procedure to activate self and put on the bf_subtasks chain if leader */
- /*------------------------------------------------------------------------*/
-
- static void activate_self(int task_master, int task_bfp) /*;activate_self*/
- {
- int *ptr; /* memory address */
- int i, val1, val2, tmp_base, tmp_off; /* temporary base */
-
- if (MY_BROTHER != NULL_BROTHER) /* I am leader of RTS */
- MY_BROTHER =
- FAS(&(((struct bf *)(STACK(task_master)+task_bfp))->bf_subtasks), tp);
-
- if (MY_STATUS == TERMINATED || MY_ABNORMAL) { /* may have been aborted */
- MY_STATUS = TERMINATED;
- multisignal(MY_PARENT, NO_EVENT);
- schedule(NO_ACTION);
- return;
- }
-
- MY_MASTER_TASK = task_master;
- MY_MASTER_BLOCK= task_bfp;
- MY_NUM_NOTERM = 1; /* myself */
- MY_NUM_DEPS = 1; /* myself */
-
- ptr = ADDR(MY_TBASE, MY_TOFF-WORDS_TASK);
- tmp_base = TASK(ptr)->body_base;
- tmp_off = TASK(ptr)->body_off;
- ptr = ADDR(tmp_base, tmp_off);
- cs = *ptr;
- if (cs < 1) { /* Not elaborated !! TBSL */
- TCB_EXCEPTION(MY_PARENT) = PROGRAM_ERROR;
- multisignal(MY_PARENT, PROGERR_EVENT);
- schedule(NO_ACTION);
- return;
- }
- ip = TASK_CODE_OFFSET;
-
- /* reserve space for local variables */
- /* note: this could be somehow optimized by just moving */
- /* the Top of Stack.... */
-
- #ifdef ALIGN_WORD
- val1 = get_int((int *)(code_segments[cs] + code_seglen[cs] - sizeof(int)
- -1));
- #else
- val1 = *(int *)(code_segments[cs] + code_seglen[cs] - sizeof(int) - 1);
- #endif
- for (i = 0; i < val1; i++)
- PUSH(0);
-
- i = cur_stackptr + NB_REGISTERS - 1;
- PUSH(i); /* SFP -- this is a trap */
- PUSH(cs); /* CS */
- PUSH(lin); /* LIN*/
- PUSH(ip); /* IP */
- sfp = cur_stackptr + 1;
- /* copy relay set */
-
- val2 = *++ptr * 2; /* length */
- for (i = 0; i < val2; i++)
- PUSH(*++ptr);
-
- /* Dummy block frame for trapping exceptions */
- PUSH(0); /* bfp */
- bfp = cur_stackptr;
- PUSHP(0L); /* data_link */
- PUSHP(0L); /* tasks_declared */
- PUSH(1); /* num_noterm */
- PUSH(1); /* num_deps */
- PUSH(NULL_TASK);/* subtasks */
- PUSH(2); /* exception vector */
- cur_code = code_segments[cs];
-
- /* This must occur after possible to detect all errors */
-
- INC(TCB_NUM_NOTERM(MY_MASTER_TASK));
- INC(TCB_NUM_DEPS(MY_MASTER_TASK));
- INC(BF_NUM_NOTERM(MY_MASTER_TASK, MY_MASTER_BLOCK));
- INC(BF_NUM_DEPS(MY_MASTER_TASK, MY_MASTER_BLOCK));
-
- #ifdef TRACE
- if (tasking_trace) {
- printf("task %d activating task %d\n",task_master,tp);
- }
- #endif
- }
-
-
- /*-------------------------------------------------------------------------*/
- /* END ACTIVATION */
- /* Procedure called at the end of activation to signal the parent task */
- /* that everything is OK (or not as the case may be) */
- /*-------------------------------------------------------------------------*/
-
- void end_activation(int term_code) /*;end_activation*/
- {
- /* after we passed the end of activation, the exception vector */
- /* of the very first block shall designate the task_trap. */
-
- BF_HANDLER(tp, MY_PREVIOUS_BFP) = 2;
- if (term_code == 0) {
- TCB_EXCEPTION(MY_PARENT) = TASKING_ERROR;
- DEC(RTS_MULT(MY_RTS_ITEM));
- RTS_TCBS(MY_RTS_ITEM, MY_ID) = NULL_TASK;
- multisignal(MY_PARENT, TASKERR_EVENT);
- }
- else {
- MY_STATUS = ACTIVE;
- multisignal(MY_PARENT, NO_EVENT);
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* A B O R T R O U T I N E S */
- /*--------------------------------------------------------------------------*/
-
- /*--------------------------------------------------------------------------*/
- /* ABORT */
- /* Procedure used to abort nr_task tasks whose numbers are on the stack */
- /*--------------------------------------------------------------------------*/
-
- void abort(int nr_tasks) /*;abort*/
- {
- int current; /* pointer to the task currently aborting */
- int i; /* temporary loop index */
-
- for (i = 0; i < nr_tasks; i++) {
- POP(current);
- kill(current);
- }
- if (MY_ABNORMAL) /* Suicide! */
- abortme();
- }
-
- /*--------------------------------------------------------------------------*/
- /* KILL */
- /* Procedure to abort task, after having aborted its dependent tasks. If */
- /* nobody in the family is engaged in a rendezvous, then the task is made */
- /* terminated and space of dependent tasks is released. This procedure does */
- /* not call WAIT: this has to be done by the caller if it aborts itself. */
- /*--------------------------------------------------------------------------*/
-
- static void kill(int task) /*;kill*/
- {
- int task2, dep, i, j, block;
-
- /* STEP 1: Check status etc. of task being aborted ... */
-
- if (INC(TCB_ABNORMAL(task)) || (TCB_STATUS(task) == TERMINATED)) {
- /* more than one task trying to abort it, must clean up ... */
- #ifdef TRACE
- if (tasking_trace)
- printf("task %d aborting terminated task %d\n",tp,task);
- #endif
- DEC(TCB_ABNORMAL(task)); /* avoid overflow */
- return;
- }
-
- #ifdef TRACE
- if (tasking_trace && TCB_ABNORMAL(task))
- printf("task %d aborting task %d\n",tp,task);
- #endif
-
- /* STEP 2: Kill all dependent tasks by block
- * I can spawn tasks and use a multi-wait count here to make sure done
- */
- if (TCB_BLOCK_PTR(task) == NULL) block = 0; /* no yet activated ... */
- else block = *(TCB_BLOCK_PTR(task));
- while (block != 0) {
- task2 = BF_SUBTASKS(task, block);
- while (task2 != NULL_TASK) {
- i = 0;
- j = 0;
- while (i < RTS_MULT(TCB_RTS_ITEM(task2)))
- if ((dep=RTS_TCBS(TCB_RTS_ITEM(task2),j++)) != NULL_TASK) {
- i++;
- kill(dep);
- }
- task2 = TCB_BROTHER(task2);
- }
- block = BF_PREVIOUS_BFP(task, block);
- }
- /* STEP 3: Perform cleanup. Try to disable task if it waiting, if
- * successful then must signal. Otherwise it will discover itself when
- * awakens.
- */
- switch (TCB_STATUS(task)) {
- case COMPLETED :
- case ACTIVATING :
- break;
- case SELECTING_TERM:
- case SELECTING_NOTERM:
- if (FAS(&(TCB_RDV(task)),0) == 1)
- make_ready(task, ABORT_EVENT);
- break;
- case TIMED_RDV:
- case CALLING_RDV:
- if (eremove(task)) make_ready(task, ABORT_EVENT);
- break;
- case WAIT:
- if (remove_io(&(TCB_IO_ITEM(task))))
- make_ready(task, ABORT_EVENT);
- break;
- default:
- raise(SYSTEM_ERROR, "Aborting task in unknown state");
- break;
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* ABORTME */
- /* Task has discovered it was aborted. kids in select term have been sig- */
- /* nalled. Completed kids will be notified when their kids are through. */
- /* My terminated kids are already done. */
- /*--------------------------------------------------------------------------*/
-
- static void abortme() /*;abortme */
- {
- purge_rdv(tp);
- if ((MY_STATUS != QUIESCENT) && (DEC(MY_NUM_NOTERM) != 1)) {
- MY_STATUS = COMPLETED;
- schedule(ABORT_ONE);
- }
- else post_abort_one();
- }
-
- static void post_abort_one() /*;post_abort_one*/
- {
- if (MY_STATUS != QUIESCENT)
- check_termination(MY_MASTER_TASK, MY_MASTER_BLOCK);
- task_term(); /* not strictly needed for abort, just "term-wave" */
- }
-
- /*--------------------------------------------------------------------------*/
- /* T E R M I N A T I O N */
- /* */
- /* The following set of routines maintain the counters and data structures */
- /* used to perform task temination. They are divided into two sets: those */
- /* used in the termination of blocks and subprograms and those used in the */
- /* termination of tasks. */
- /*--------------------------------------------------------------------------*/
-
-
- /*--------------------------------------------------------------------------*/
- /* COMPLETE TASK */
- /* This is the last instruction in a task's instruction stream */
- /*--------------------------------------------------------------------------*/
-
- void complete_task() /*;complete_task*/
- {
- MY_STATUS = COMPLETED;
- if (DEC(MY_NUM_NOTERM) != 1) schedule(COMPLETE_TASK_ONE);
- else post_complete_task_one();
- }
-
- static void post_complete_task_one() /*;post_complete_task_one*/
- {
- check_termination(MY_MASTER_TASK, MY_MASTER_BLOCK);
- if (INC(MY_ABNORMAL) == 0)
- task_term();
- else if (DEC(MY_NUM_DEPS) != 1) schedule(FREE_TASK_SPACE);
- else free_task_space();
- }
-
- /*--------------------------------------------------------------------------*/
- /* COMPLETE BLOCK */
- /*--------------------------------------------------------------------------*/
-
- void complete_block() /*;complete_block*/
- {
- MY_STATUS = COMPLETE_BLOCK;
- if (DEC(MY_BF_NUM_NOTERM) != 1)
- schedule(COMPLETE_BLOCK_ONE);
- else post_complete_block();
- }
-
- static void post_complete_block() /*;post_complete_block*/
- {
- if (MY_ABNORMAL == 0)
- tell_termination(*MY_BLOCK_PTR);
- if (DEC(MY_BF_NUM_DEPS) != 1) schedule(COMPLETE_BLOCK_TWO);
- else post_complete_block_two();
- }
-
- static void post_complete_block_two() /*;post_complete_block_two*/
- {
- MY_STATUS = ACTIVE;
- }
-
- /*--------------------------------------------------------------------------*/
- /* TERMINATION SUPPORT ROUTINES */
- /*--------------------------------------------------------------------------*/
-
- static void task_term() /*;task_term*/
- {
- int block;
-
- if (DEC(MY_NUM_DEPS) != 1) {
- block = *MY_BLOCK_PTR;
- while (block != 0) {
- tell_termination(block);
- block = BF_PREVIOUS_BFP(tp, block);
- }
- schedule(FREE_TASK_SPACE);
- }
- else free_task_space();
- }
-
- static void free_task_space() /*;free_task_space */
- {
- int block;
-
- block = *MY_BLOCK_PTR;
- while (block != 0) {
- uncreate_tasks(BF_SUBTASKS(tp, block));
- block = BF_PREVIOUS_BFP(tp, block);
- }
- MY_STATUS = TERMINATED;
- check_done(MY_MASTER_TASK, MY_MASTER_BLOCK);
- schedule(NO_ACTION);
- }
-
- /* Recursive procedure to check master tasks and blocks called by quiescent */
- /* dependent of "task" and "block" */
-
- static void check_termination(int task, int block) /*;check_termination */
- {
- if ((task != NULL_TASK) && (task != 0) && (DEC(TCB_NUM_NOTERM(task)) == 1))
- if ((TCB_STATUS(task) == COMPLETED) && (INC(TCB_FIRST(task)) == 0)) {
- if (TCB_NUM_NOTERM(task) == 0)
- make_ready(task, TERMINATE_EVENT);
- }
- else
- check_termination(TCB_MASTER_TASK(task),TCB_MASTER_BLOCK(task));
-
- if ((block != 0) && (DEC(BF_NUM_NOTERM(task, block)) == 1))
- make_ready(task, TERMINATE_EVENT);
- }
-
- static void check_done(int task, int block) /*;check_done*/
- {
- int dd;
- dd = DEC(TCB_NUM_DEPS(task));
- if ((DEC(BF_NUM_DEPS(task, block)) == 1) || (dd == 1))
- make_ready(task, NO_EVENT);
- }
-
- static void check_unterm() /*;check_unterm*/
- {
- if (INC(MY_NUM_NOTERM) == 0) {
- INC(TCB_NUM_NOTERM(MY_MASTER_TASK));
- INC(BF_NUM_NOTERM(MY_MASTER_TASK, MY_MASTER_BLOCK));
- }
- }
-
- static void tell_termination(int block) /*;tell_termination*/
- {
- int i, j, dep, task;
-
- task = BF_SUBTASKS(tp, block);
- while (task != NULL_TASK) {
- i = 0;
- j = 0;
- while (i < RTS_MULT(TCB_RTS_ITEM(task)))
- if ((dep=RTS_TCBS(TCB_RTS_ITEM(task),j++)) != NULL_TASK) {
- i++;
- if ((TCB_STATUS(dep) != TERMINATED)
- && (INC(TCB_ABNORMAL(dep)) == 0))
- make_ready(dep, TERMINATE_EVENT);
- }
- task = TCB_BROTHER(task);
- }
- }
-
- /* Procedure used when an exception is raised to terminate tasks that have
- * created, but not yet activated.(LRM 9.3(4)) Such tasks may have pending
- * rendezvous. All tasks linked to task frames depending on the current BFP
- * are made terminated. They are linked together and put on bf_subtasks in
- * order to release their space when the block is exited.
- */
-
- void terminate_unactivated() /*;terminate_unactivated */
- {
- int current, next, removed, dep, i, j;
- int *task_frame;
-
- task_frame = MY_TASKS_DECLARED;
- if (task_frame != (int *)0) {
- removed = NULL_TASK;
- *(task_frame - WORDS_PTR - 1) = -(*(task_frame - WORDS_PTR - 1));
- for (;;) {
- current = *task_frame;
- if (current != NULL_TASK) {
- for (;;) {
- i = 0;
- j = 0;
- while (i < RTS_MULT(TCB_RTS_ITEM(current)))
- if ((dep=RTS_TCBS(TCB_RTS_ITEM(current),j++))
- != NULL_TASK) {
- i++;
- TCB_STATUS(dep) = TERMINATED;
- purge_rdv(dep);
- }
- next = TCB_BROTHER(current);
- if (next == NULL_TASK)
- break;
- current = next;
- }
-
- /* we are still in the context of the last task on the chain
- * merge the chain with previous one
- */
-
- TCB_BROTHER(current) = removed;
- removed = *task_frame;/* head of the chain */
- }
- task_frame = *((int **)(task_frame - WORDS_PTR));
- if (task_frame == (int *)0)
- break;
- }
-
- if (removed != NULL_TASK) /* merge on bf_subtasks to release space */
- MY_SUBTASKS = union_tasks_declared(removed, MY_SUBTASKS);
- MY_TASKS_DECLARED = (int *)0;
- }
- }
-
- /* Procedure to raise TASKING_ERROR in all tasks waiting for or engaged */
- /* in a rendezvous with the currently active task */
-
- void purge_rdv(int curr) /*;purge_rdv */
- {
- int task, i;
-
- for (i = 1; i <= TCB_NUM_ENTRIES(curr); i++)
- while (ENTRY_COUNT(TCB_ENTRY(curr,i)) > 0) {
- DEC(ENTRY_COUNT(TCB_ENTRY(curr,i)));
- if ((task = eget(TCB_ENTRY(curr,i))) != NULL_TASK) {
- TCB_EXCEPTION(task) = TASKING_ERROR;
- make_ready(task, TASKERR_EVENT);
- }
- }
-
- task = TCB_SERVICED(curr);
- while (task != NULL_TASK) {
- TCB_EXCEPTION(task) = TASKING_ERROR;
- make_ready(task, TASKERR_EVENT);
- task = TCB_NEXT(task);
- }
- }
-
- static void uncreate_tasks(int task_list) /*;uncreate_tasks */
- {
- int task, next, i, j;
- struct rts_item *item;
-
- task = task_list;
- while (task > 0) {
- next = TCB_BROTHER(task);
- item = TCB_RTS_ITEM(task);
- i = 0;
- j = 0;
- while (i < RTS_MULT(item))
- if ((task = RTS_TCBS(item,j++)) != NULL_TASK) {
- #ifdef TRACE
- if (signal_trace)
- printf("task %d releasing space of %d \n ", tp, task);
- #endif
- i++;
- efree((char *) STACK(task));
- STACK(task) = (int *) 0;
- ORIG(task) = NULL_TASK;
- }
- task = next;
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* T I M E M A N A G E M E N T */
- /* */
- /* The following set of routines perform time management for "delay" */
- /* statements. The chain of waiting tasks associated with each PE */
- /* is sorted in ascending time of end of delay. The time associated */
- /* with each entry in the chain in the amount of time it must wait after */
- /* the previous entry is done (e.g. the wait time is kept incrementally) */
- /* A global variable "next_clock" keeps the absolute time value of the next*/
- /* interrupt. When the interrupt processing routine is called all interupt*/
- /* which are now ready -or- were previously ready are made ready. */
- /* Io_items are freed by either the owning task or the interrupt */
- /* process. If the io_item has been disabled, it is freed by the catcher */
- /* routine. Otherwise it is freed by the process when it awakens from */
- /* make ready. */
- /*--------------------------------------------------------------------------*/
-
-
- static void my_set_timer(struct io_item_type *item, long now) /*;my_set_timer*/
- {
- next_clock = II_DELTA(item) + now;
- }
-
- static int long my_reset_timer(long now) /*;my_reset_timer*/
- {
- last_time = now;
- return(next_clock - now);
- }
-
- /*--------------------------------------------------------------------------*/
- /* DELAY_STMT */
- /* Guarantees that all wait times are positive (provided for compatability)*/
- /* Note that it recognizes three types of delays: */
- /* 0 - signifies simply a request to context switch */
- /* ENDLESS - signifies simply relinquishing the CPU */
- /* other - actual create a timer chain element and chain it */
- /*--------------------------------------------------------------------------*/
-
- void delay_stmt(long delay_time) /*;delay_stmt*/
- {
- long effective_delay;
-
- effective_delay = delay_time >= 0 ? delay_time : 0;
- #ifdef TRACE
- if (rendezvous_trace) {
- printf("task %d delaying %ld effective delay %ld \n", tp,
- delay_time, effective_delay);
- }
- #endif
- if (effective_delay == (long) ENDLESS)
- schedule(NO_ACTION);
- else if (effective_delay == 0)
- context_switch();
- else
- wait(effective_delay, WAIT);
- }
-
- /*--------------------------------------------------------------------------*/
- /* WAIT */
- /* Routines which makes the task wait for the delay time */
- /*--------------------------------------------------------------------------*/
-
- static void wait(long delay, int action) /*;wait*/
- {
- MY_STATUS = WAIT;
- if (MY_ABNORMAL && MY_STATUS != TERMINATED) {
- abortme();
- return;
- }
- MY_IO_ITEM = chain(tp, delay);
- schedule(action);
- }
-
- static void post_wait() /*;post_wait*/
- {
- MY_STATUS = ACTIVE;
- if (MY_ABNORMAL)
- abortme();
- }
-
- /*--------------------------------------------------------------------------*/
- /* CATCHER */
- /* Routine called when a timer interrupt occurs. */
- /*--------------------------------------------------------------------------*/
-
- void clock_interrupt(long now_time) /*;clock_interrupt*/
- {
- catcher(now_time);
- context_switch();
- }
-
- static void catcher(long now_time) /*;catcher*/
- {
- struct io_item_type *item;
- long time;
-
- /* Time Out head of io_item list and all others following with delta 0 */
- time = now_time - last_time;
- last_time = now_time;
-
- while ((clock_head != NULL) && (II_DELTA(clock_head)<=time)) {
- item = II_NEXT(clock_head); /* TO others waiting in interval */
- time -= II_DELTA(clock_head);
- check_free(clock_head);
- clock_head = item;
- }
-
- /* remove interior deletes from head of list */
-
- item = clock_head;
- while ((clock_head != NULL) && (II_FLAG(clock_head) == 0)) {
- time -= II_DELTA(clock_head);
- item = II_NEXT(clock_head);
- efree((char *) clock_head);
- clock_head = item;
- }
- if (clock_head != NULL) {
- II_DELTA(clock_head) -= time;
- my_set_timer(clock_head, now_time);
- }
- else next_clock_flag = FALSE;
- }
-
- /*--------------------------------------------------------------------------*/
- /* CHAIN */
- /* Called when execute a delay. Add task to timeout chain */
- /*--------------------------------------------------------------------------*/
-
- static struct io_item_type *chain(int task, long delay) /*;chain*/
- {
- long my_reset_time(), itime();
- long now;
- struct io_item_type *new_item, *item, *prev_item;
-
- now = itime() + time_offset;
- if (TCB_IO_ITEM(task) == NULL) {
- new_item=(struct io_item_type *)malloc(sizeof(struct io_item_type));
- if (new_item == (struct io_item_type *)0) {
- raise(STORAGE_ERROR, "Allocating space for timer chain");
- return(NULL);
- }
- }
- else new_item = TCB_IO_ITEM(task);
- if (clock_head == NULL) {
- clock_head = new_item;
- II_TASK(new_item) = task;
- II_FLAG(new_item) = TCB_STATUS(task);
- II_NEXT(new_item) = NULL;
- II_DELTA(new_item) = delay;
- }
- else {
- II_DELTA(clock_head) = my_reset_timer(now); /* time in intval*/
- II_TASK(new_item) = task;
- II_FLAG(new_item) = TCB_STATUS(task);
-
- /* seq. search for new_item's position based on relative deltas */
- if (delay <= II_DELTA(clock_head)) {
- II_NEXT(new_item) = clock_head;
- II_DELTA(new_item) = delay;
- II_DELTA(clock_head) -= delay;
- clock_head = new_item;
- }
- else {
- item = clock_head;
- prev_item = clock_head;
- while ((item != NULL) && (delay-II_DELTA(item) > 0)) {
- delay -= II_DELTA(item);
- prev_item = item;
- item = II_NEXT(item);
- }
- II_NEXT(new_item) = item;
- II_NEXT(prev_item) = new_item;
- II_DELTA(new_item) = delay;
- if (item != NULL)
- II_DELTA(item) -= delay;
- }
- }
- next_clock_flag = TRUE;
- my_set_timer(clock_head, now);
- return(new_item);
- }
-
- /*--------------------------------------------------------------------------*/
- /* CHECK FREE */
- /* Called by catcher to see if timeout is possible */
- /*--------------------------------------------------------------------------*/
-
- static void check_free(struct io_item_type *item) /*;check_free*/
- {
- int value;
-
- value = get_io(item);
- if (value > 0) /* anything else being waited for has been disble */
- make_ready(II_TASK(item), TIMER_EVENT);
-
- /* Originally we were going to deallocate the io_item. This was later
- * changed EXCEPT in the case where it was already disabled ...
- */
- else if (value == -1)
- efree((char *) item);
- }
-
- /*--------------------------------------------------------------------------*/
- /* GET IO */
- /*--------------------------------------------------------------------------*/
-
- static int get_io(struct io_item_type *item) /*;get_io*/
- {
- int wake;
-
- switch (FAS(&(II_FLAG(item)),TIMER_EVENT)){ /* was task's status */
- case DISABLED_EVENT:
- wake = -1;
- break; /* already gone */
- case TIMED_RDV :
- wake = eremove(II_TASK(item));
- FAS(&(II_FLAG(item)), 0);
- break;
- case SELECTING_NOTERM: /* disable */
- case SELECTING_TERM:
- wake = FAS(&(TCB_RDV(II_TASK(item))),0);
- FAS(&(II_FLAG(item)), 0);
- break;
- case WAIT :
- wake = 1;
- break; /* must wake up */
- case 0 :
- wake = -1;
- break; /* innner remove*/
- default :
- raise(SYSTEM_ERROR, "Unexpected event");
- break;
- }
- II_FLAG(item) = 0;
- return wake;
- }
-
-
- /*--------------------------------------------------------------------------*/
- /* DISABLE IO */
- /* Called when rdv ti disable timeouts. No timeouts possible, but avoids */
- /* race (that item.flag may be gotten, rendezvous, then RDV reset for next */
- /* rendezvous). */
- /*--------------------------------------------------------------------------*/
-
- static void disable_io(struct io_item_type **item_ptr) /*;disable_io*/
- {
- struct io_item_type *item;
-
- item = *item_ptr;
- if (item != (struct io_item_type*)0) {
- if (FAS(&(II_FLAG(item)),DISABLED_EVENT) == TIMER_EVENT) {
- /* Timeout in progress, wait for it to end then delete */
- while (II_FLAG(item) != 0) continue;
- efree((char *) item);
- }
- *item_ptr =(struct io_item_type *)0 ;
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* REMOVE IO */
- /* Called when abort in delay */
- /*--------------------------------------------------------------------------*/
-
- static int remove_io(struct io_item_type **item_ptr) /*;remove_io */
- {
- struct io_item_type *item;
-
- item = *item_ptr;
- if (item != (struct io_item_type *)0)
- if (FAS(&(II_FLAG(item)),DISABLED_EVENT) == TIMER_EVENT) {
- efree((char *) item); /* too late, has timed out ... */
- *item_ptr = (struct io_item_type *)0;
- return 0;
- }
- return 1;
- }
-
- /*--------------------------------------------------------------------------*
- * S C H E D U L E R *
- * *
- * The following set of routines perform decentrallized scheduling based *
- * on "run-until-blocked". Because the interpreter simply simulates tasks *
- * it is absolutely necessary that once "schedule" is call and control is *
- * passed to a new virtual task, that the interpretor return to the highest*
- * level without executing any other code (excepting the cleanup routines *
- * called in "schedule". Thus any statement which (recursively) executes *
- * "schedule" must be immediately succeeded by a return statement. The *
- * "dangerous" routines in the system are: *
- * - abort - abortme *
- * - wait - post_wait - delay_stmt *
- * - complete_block - task_complete - termflags_on_wait *
- * - entry_call - post_entry_call - end_rendezvous *
- * - selective_wait - post_selective_wait *
- *--------------------------------------------------------------------------*/
-
- /*--------------------------------------------------------------------------*
- * SCHEDULE *
- * Block the currently executing task and select a new task to run. *
- *--------------------------------------------------------------------------*/
-
- static void schedule(int action) /*;schedule*/
- {
- int done, p, i;
- struct rts_item *item;
-
- MY_ACTION = action;
- if (DEC(MY_NUM_EVENTS) > 0) {
- finish_action(MY_RTS_ITEM); /* an event is waiting */
- return;
- }
- if (action == CONTEXT_SWITCH) INC(MY_NUM_EVENTS);
- done = FALSE;
- while (!done) {
- for (p=MAX_PRIO-1;p>=0;p--) {
- item = dequeue(ready_queue[p], &i);
- if (item != (struct rts_item *)0) {
- switch(RTS_TYPE(item)) {
- case ACTIVATE :
- case READY :
- #ifdef TRACE
- if (signal_trace) {
- printf("task %d switching to task %d \n \n",
- tp,item->tcbs[i]);
- }
- #endif
- highest_priority = p;
- transfer(item->tcbs[i]);
- done = TRUE;
- break;
- case CREATE :
- create_task(RTS_TEMPL_BASE(item),
- RTS_TEMPL_OFF(item),item,i);
- break;
- default :
- raise(SYSTEM_ERROR, "Unexpected type");
- break;
- }
- break; /* out of for loop */
- }
- else if (p == 0) done = TRUE; /* end loop (error) */
- }
- }
-
- if (item == (struct rts_item *)0)
- { /* No active task was found, error condition */
- MY_STATUS = ACTIVE;
- raise(SYSTEM_ERROR, "No activatable task");
- MY_EXCEPTION = 0;
- return;
- }
- finish_action(item);
- }
-
- /* Now the new task is processing. It must perform the appropriate */
- /* cleanup routine based on the action which it was performing before */
- /* making the call to the scheduler ... */
-
- static void finish_action(struct rts_item *item) /*;finish_action*/
- {
- int open_en[MAX_ALTS], accept_index[MAX_ALTS];
- long sleep_time;
- long itime();
-
- switch (MY_ACTION) {
- case WAIT :
- post_wait();
- break;
- case SELECTIVE_WAIT :
- post_selective_wait(0, 0, open_en, accept_index);
- break;
- case ENTRY_CALL :
- post_entry_call();
- break;
- case COMPLETE_BLOCK_ONE :
- post_complete_block();
- break;
- case COMPLETE_BLOCK_TWO :
- post_complete_block_two();
- break;
- case ABORT_ONE :
- post_abort_one();
- break;
- case COMPLETE_TASK_ONE :
- post_complete_task_one();
- break;
- case FREE_TASK_SPACE :
- free_task_space();
- break;
- case DONE_ACTIVATION :
- done_activation();
- break;
- case DONE_CREATION :
- done_creation();
- break;
- case ACTIVATE_SELF :
- activate_self(RTS_MASTER_TASK(item), RTS_MASTER_BLOCK(item));
- break;
- case CONTEXT_SWITCH :
- case NO_ACTION :
- break;
- default :
- raise(SYSTEM_ERROR, "Tasks awakened in an unknown state");
- MY_EXCEPTION = 0;
- break;
- }
-
- /* then see if someone else waiting to be scheduled */
-
- if (tp == 0) { /* we schedule IDLE */
- if (next_clock_flag) { /* exists task waiting? */
- if (simul_time_flag) /* speed up time! */
- time_offset += next_clock - itime();
- else if ((sleep_time=(next_clock-itime())/1000L) > 1L)
- sleep((unsigned)sleep_time);
- }
- else if ((MY_ACTION != DONE_CREATION) && (MY_ACTION != DONE_ACTIVATION))
- raise(PROGRAM_ERROR, "System inactive(deadlock)");
- }
- MY_ACTION = NO_ACTION;
- }
-
- /*--------------------------------------------------------------------------*/
- /* MAKE READY */
- /* Moves the specified task to the ready queue setting it's event flag. */
- /*--------------------------------------------------------------------------*/
-
- static void make_ready(int task, int event) /*;make_ready*/
- {
- if (event != NO_EVENT) /* Mulitple events may overwrite (COMP,TERM,ABORT) */
- TCB_EVENT(task)=event; /* but task can tell if correct one happened */
-
- if (INC(TCB_NUM_EVENTS(task)) == -1) enqueue(TCB_PRIO(task), task);
- }
-
- /*---------------------------------------------------------------------------*/
- /* TRANSFER */
- /* Transfers control from the currently executing task to the one specified */
- /*---------------------------------------------------------------------------*/
-
- static void transfer(int new_task) /*;transfer*/
- {
- PUSH(exr);
- PUSH(lin);
- PUSH(sfp);
- PUSH(cs);
- PUSH(ip);
- PUSH(bfp); /* ----------MUST be on top of stack---------- */
- MY_BLOCK_PTR = (int *) (cur_stack+cur_stackptr);
- STACKPTR(tp) = cur_stackptr;
-
- tp = new_task;
- cur_stack = STACK(tp);
- cur_stackptr = STACKPTR(tp);
- POP(bfp);
- MY_BLOCK_PTR = &bfp;
- POP(ip);
- POP(cs);
- POP(sfp);
- POP(lin);
- POP(exr);
- cur_code = code_segments[cs];
- }
-
- /*--------------------------------------------------------------------------*
- * R E N D E Z V O U S *
- * *
- * The following set of procedures are used to performs rendezvous. They *
- * are divided into two sets: those used to perform a "select" call and *
- * those used to execute an "entry" call. They make use of the parallel *
- * queue routines and use the "rdv" flag, the array of "entry" records, the *
- * "io_item" when processing delays as well as checking and setting the *
- * "status" and "abnormal" fields in the caller and/or owner task's TCB. *
- * The routines which are called by the interpretor directly are: *
- * - selective_wait *
- * - end_rendezvous *
- * - entry_call *
- * There calling interface (stack upon entry and return as well as parms. *
- * passed are identical to the original system. *
- *--------------------------------------------------------------------------*/
-
-
- /*--------------------------------------------------------------------------*
- * SELECTIVE WAIT *
- *--------------------------------------------------------------------------*/
-
- void selective_wait(int num_alts) /*;selective_wait */
- {
- int alt_kind; /* delay, terminate or accept */
- long delay_time; /* value of delay */
- long min_delay; /* minimum delay */
- int family; /* family index of entry */
- int member; /* member of family */
- int guard; /* guard of statement (boolean) */
- int open_ai[MAX_ALTS]; /* list of open accept indices */
- int open_en[MAX_ALTS]; /* list of open entry numbers */
- int num_open_alts; /* number of open alternatives */
- int delay_index; /* index to delay alt. (or -1) */
- int term_index; /* index to terminate alt. (or -1) */
- int accept_index; /* index to chosen alternative */
- int caller; /* chosen task id of caller */
- struct entry_type *entry; /* pointer to entry */
- int i, temp;
-
-
- /* STEP 1: Build a set of open alternatives and determine the shortest
- * delay and the index set of the terminate (if any)
- */
-
- term_index = -1;
- min_delay = ENDLESS;
- delay_index = -1;
-
- if (num_alts == 0) { /* simple accept */
- POP(member);
- POP(family);
- num_open_alts = 1;
- open_ai[0] = 1;
- open_en[0] = entry_number(tp, family, member);
- }
- else num_open_alts = 0;
-
- for (accept_index = num_alts; accept_index >= 1; accept_index --) {
- POP(alt_kind);
- if (alt_kind == 1) { /* accept */
- POP(member);
- POP(family);
- POP(guard);
- if (guard == 1) {
- open_ai[num_open_alts] = accept_index;
- open_en[num_open_alts++] = entry_number(tp, family, member);
- }
- }
- else if (alt_kind == 2) { /* delay */
- POPL(delay_time);
- POP(guard);
- if (guard == 1)
- if (delay_index == -1 || delay_time<min_delay) {
- min_delay = (delay_time>=0L) ? delay_time : 0L;
- delay_index = accept_index;
- }
- }
- else if (alt_kind == 3) { /* terminate */
- POP(guard);
- if (guard == 1)
- term_index = accept_index;
- }
- else raise(SYSTEM_ERROR,
- "Unknown alternative in select statement");
- }
-
- /* STEP 2: Check local status, terminatability, guards, rdv flag etc. */
-
- if (MY_ABNORMAL) {
- abortme();
- return;
- }
- MY_STATUS = SELECTING_NOTERM;
-
- evaluate_guards(open_en, num_open_alts);
-
- FAS(&(MY_RDV), 1); /* an rdv is now possible */
- if (MY_ABNORMAL && (FAS(&(MY_RDV), 0) == 1)) {
- abortme();
- return;
- }
-
- /* STEP 3: See if can start an immediate rendezvous */
-
- if (num_open_alts > 0) {
- i = 0;
- while (MY_RDV && (i < num_open_alts)) {
- temp = open_ai[i];
- entry = MY_ENTRY(open_en[i++]);
- if (ENTRY_GUARD(entry)) {
- del_lock(&(LOCK(entry)));
- if ((DEC(ENTRY_COUNT(entry)) > 0) && (FAS(&(MY_RDV),0) == 1)) {
- /* a task is waiting and got flag so rdv is possible */
- caller = eget(entry);
- del_unlock(&(LOCK(entry)));
- close_guards(open_en, num_open_alts);
- TCB_SAVE_PRIO(caller) = MY_PRIO;
- MY_PRIO = MAX(TCB_SAVE_PRIO(caller),MY_PRIO);
- accept_rdv(caller,temp,num_alts);
- return;
- }
- else { /* we did not - disable the rendezvous... */
- INC(ENTRY_COUNT(entry));
- del_unlock(&(LOCK(entry)));
- }
- }
- }
- }
-
- /* STEP 4: Unfortunately, couldn't perform rendezvous. Now either detect
- * an error condition, try to terminate or delay
- */
-
- if ((num_open_alts == 0) && (delay_index == -1) && (term_index == -1)){
- raise(PROGRAM_ERROR, "No open alternatives in select");
- return;
- }
-
- if (MY_RDV == 0) { /* have been aborted or called, busywait */
- while ((MY_EVENT != RDV_EVENT) && (!MY_ABNORMAL)) continue;
- post_selective_wait(1, num_open_alts, open_en, open_ai);
- }
- else if ((min_delay == 0) && FAS(&(MY_RDV), 0) == 1) {
- MY_EVENT = TIMER_EVENT;
- open_en[num_open_alts] = TIMER_EVENT;
- open_ai[num_open_alts++] = delay_index;
- post_selective_wait(1, num_open_alts, open_en, open_ai);
- return;
- }
- else {
- if (num_alts != 0) /* push open entry table on stack */
- for (i=0;i<num_open_alts;i++) {
- PUSH(open_en[i]);
- PUSH(open_ai[i]);
- }
- if (delay_index != -1) {
- PUSH(TIMER_EVENT);
- PUSH(delay_index);
- num_open_alts++;
- }
- if (term_index != -1) {
- PUSH(TERMINATE_EVENT);
- PUSH(term_index);
- MY_STATUS = SELECTING_TERM;
- if (DEC(MY_NUM_NOTERM) == 1) /* am quiescent */
- check_termination(MY_MASTER_TASK, MY_MASTER_BLOCK);
- num_open_alts++;
- }
- if (num_alts == 0) /* simple accept */
- PUSH(0);
- else
- PUSH(num_open_alts);
-
- #ifdef TRACE
- if (rendezvous_trace)
- printf("task %d waiting for a rendezvous \n", tp);
- #endif
- if (min_delay == ENDLESS) schedule(SELECTIVE_WAIT);
- else wait(min_delay, SELECTIVE_WAIT);
- }
- }
-
-
- /*--------------------------------------------------------------------------*/
- /* EVALUATE GUARDS */
- /*--------------------------------------------------------------------------*/
-
- static void evaluate_guards(int open_en[], int num_open) /*;evaluate_guards*/
- {
- int i;
- struct entry_type *entry;
-
- for (i=0;i< num_open;i++) {
- entry = MY_ENTRY(open_en[i]);
- del_lock(&(LOCK(entry)));
- ENTRY_GUARD(entry) = 1;
- del_unlock(&(LOCK(entry)));
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* CLOSE GUARDS */
- /*--------------------------------------------------------------------------*/
-
-
- static void close_guards(int open_en[], int num_open) /*;close_guards*/
- {
- int i;
- struct entry_type *entry;
-
- for (i=0;i< num_open;i++) {
- if ((open_en[i] != TERMINATE_EVENT) && (open_en[i] != TIMER_EVENT)){
- entry = MY_ENTRY(open_en[i]);
- del_lock(&(LOCK(entry)));
- ENTRY_GUARD(entry) = 0;
- del_unlock(&(LOCK(entry)));
- }
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* POST SELECTIVE WAIT */
- /*--------------------------------------------------------------------------*/
-
- static void post_selective_wait(int in_mem, int num_alts, int open_en[],
- int open_ai[]) /*;post_selective_wait*/
- {
- int status, x, ai, accept_index;
-
- status = MY_STATUS;
- MY_STATUS = ACTIVE;
- if (MY_ABNORMAL) {
- if (status == SELECTING_TERM) MY_STATUS = QUIESCENT;
- abortme();
- return;
- }
- if (status == SELECTING_TERM) check_unterm();
-
- /* We must find the accept index for the selected alternative ... */
- if (MY_EVENT == TIMER_EVENT) MY_WHAT = TIMER_EVENT;
- accept_index = -1;
- if (in_mem) {
- if (num_alts != 0)
- for (x = 0; x < num_alts; x++)
- if (open_en[x] == MY_WHAT) accept_index=open_ai[x];
- }
- else {
- POP(num_alts);
- if (num_alts != 0)
- for (x =0 ; x < num_alts; x++) {
- POP(ai);
- POP(open_en[x]);
- if (open_en[x] == MY_WHAT) accept_index = ai;
- }
- }
-
- if ((num_alts != 0) && (accept_index == -1)) {
- raise(SYSTEM_ERROR, "Nonexistant alternative selected");
- return;
- }
- close_guards(open_en, num_alts);
-
- switch (MY_EVENT) {
- case TIMER_EVENT :
- if (status == WAIT) {
- efree((char *) MY_IO_ITEM);
- MY_IO_ITEM = NULL;
- }
- PUSH(accept_index);
- break;
- case RDV_EVENT :
- disable_io(&MY_IO_ITEM);
- accept_rdv(MY_WHO,accept_index,num_alts);
- break;
- default :
- raise(SYSTEM_ERROR,
- "Unexpected event in select/accept");
- break;
- }
- }
-
- /*--------------------------------------------------------------------------*/
- /* ACCEPT */
- /*--------------------------------------------------------------------------*/
-
- static void accept_rdv(int caller, int accept_index, int push_index)
- /*;accpt_rdv*/
- {
- int num_param; /* number of parameters to transfer */
- int disp, i;
-
- /* Must copy parameters over from the caller's stack */
-
- disp = STACKPTR(caller) - NB_REGISTERS;
- num_param = STACK(caller)[disp];
- disp -= num_param + 1;
- for (i = 1; i <= num_param; i++)
- PUSH(STACK(caller)[disp+i]);
-
- add_serviced(tp, caller); /* may be nested rdv need if aborted */
- if (push_index)
- PUSH(accept_index);
- }
-
- /*--------------------------------------------------------------------------*/
- /* END_RENDEZVOUS */
- /*--------------------------------------------------------------------------*/
-
- void end_rendezvous() /*;end_rendevous*/
- {
- int caller;
-
- caller = remove_serviced();
- #ifdef TRACE
- if (rendezvous_trace)
- printf("task %d end rendezvous with task %d \n",tp, caller);
- #endif
- MY_PRIO = TCB_SAVE_PRIO(caller);
- if (MY_EXCEPTION) /* propagate exception to calling task */
- TCB_EXCEPTION(caller) = MY_EXCEPTION;
-
- make_ready(caller, ENDRDV_EVENT);
- if (MY_ABNORMAL) { /* owner aborted, raise TASKING ERROR */
- TCB_EXCEPTION(caller) = TASKING_ERROR;
- abortme();
- return;
- }
- }
-
-
- /*--------------------------------------------------------------------------*/
- /* ENTRY CALL */
- /*--------------------------------------------------------------------------*/
-
- void entry_call(long delay_time, int num_param) /*;entry_call*/
- {
- struct entry_type *entry;
- int owner; /* owner of entry */
- int family; /* index of family of the entry */
- int member; /* index of member of the entry */
- int entry_num; /* actual entry number */
-
- /* STEP 1: Get calling information from the stack */
-
- member = TOSM(num_param);
- family = TOSM(num_param + 1);
- owner = TOSM(num_param + 2);
- if (owner != ORIG(owner) || STACK(owner) == (int *)0) {
- raise(TASKING_ERROR, "Call to an entry in a terminated task");
- return;
- }
- entry_num = entry_number(owner, family, member);
-
- #ifdef TRACE
- if (rendezvous_trace)
- printf("task %d calling rendezvous with task %d entry %d\n",
- tp, owner, entry_num);
- #endif
-
- /* STEP 2: Perform some error detection */
-
- if (entry_num > TCB_NUM_ENTRIES(owner)) {
- raise(SYSTEM_ERROR, "Nonexistant entry called");
- return;
- }
- if (TCB_STATUS(owner) == TERMINATED || TCB_ABNORMAL(owner)) {
- raise(TASKING_ERROR, "Call to an entry of a terminated task");
- return;
- }
-
- /* STEP 3: Set statuses */
-
- if (MY_ABNORMAL) {
- abortme();
- return;
- }
- if (delay_time == ENDLESS)
- MY_STATUS = CALLING_RDV;
- else MY_STATUS = TIMED_RDV;
- entry = TCB_ENTRY(owner, entry_num);
- PUSH(num_param);
- add_lock(&(LOCK(entry)));
-
- /* STEP 4: See if immediate rendezvous */
-
- if ((INC(ENTRY_COUNT(entry)) == 0) && ENTRY_GUARD(entry) &&
- (FAS(&(TCB_RDV(owner)), 0) == 1)) {
- /* owner is commited to a rendezvous with the caller */
- DEC(ENTRY_COUNT(entry)); /* restore counter */
- TCB_WHAT(owner) = entry_num;
- TCB_WHO(owner) = tp;
- add_unlock(&(LOCK(entry)));
- disable_io(&(TCB_IO_ITEM(owner)));
- MY_SAVE_PRIO = TCB_PRIO(owner);
- TCB_PRIO(owner) = MAX(TCB_PRIO(owner),MY_PRIO);
- make_ready(owner, RDV_EVENT);
- schedule(ENTRY_CALL); /* wait for end of rdv */
- }
-
- /* STEP 5: Cannot rendezvous immediately */
-
- else { /* cannot immediately start a rendezvous */
- if (delay_time == 0) { /* else changed to "delay 0" */
- DEC(ENTRY_COUNT(entry));
- add_unlock(&(LOCK(entry)));
- cur_stackptr -= 3;
- PUSH(0);
- }
- else {
- MY_CURR_ENTRY = entry;
- eput(entry, tp, &MY_ENTRY_ITEM);
- add_unlock(&(LOCK(entry)));
- if (MY_ABNORMAL && eremove(tp)) {
- abortme();
- return;
- }
- if (delay_time != ENDLESS)
- MY_IO_ITEM = chain(tp, delay_time);
- schedule(ENTRY_CALL);
- }
- }
- }
-
- /*-------------------------------------------------------------------*/
- /* POST ENTRY CALL */
- /*-------------------------------------------------------------------*/
-
- static void post_entry_call() /*;post_entry_call*/
- {
- int num_param; /* number of parameters */
- int i;
-
- POP(num_param);
- if (MY_ABNORMAL) {
- disable_io(&MY_IO_ITEM);
- abortme();
- return;
- }
-
- i = MY_EVENT;
- switch (MY_EVENT) {
- case TIMER_EVENT :
- efree((char *) MY_IO_ITEM);
- MY_IO_ITEM = NULL;
- cur_stackptr -= 3;
- PUSH(0);
- break;
- case ENDRDV_EVENT:
- disable_io(&MY_IO_ITEM);
- if (MY_EXCEPTION)
- raise(MY_EXCEPTION, "Exception propagated from called task");
- MY_EXCEPTION = 0;
- for (i = cur_stackptr - num_param - 3 + 1; i <= cur_stackptr; i++)
- cur_stack[i] = cur_stack[i+3];
- cur_stackptr -=3;
- if (MY_STATUS == TIMED_RDV)
- PUSH(1);
- break;
- case TASKERR_EVENT :
- raise(TASKING_ERROR, "Entry call failed: called task terminated");
- MY_EXCEPTION = 0;
- break;
- default :
- raise(SYSTEM_ERROR, "Unexpected event in entry call");
- break;
- }
- MY_STATUS = ACTIVE;
- }
-
-
- /*-----------------------------------------------------------------------*/
- /* U T I L I T Y R O U T I N E S */
- /*-----------------------------------------------------------------------*/
-
- /*--------------------------------------------------------------------------*/
- /* ENTRY QUEUE ROUTINES */
- /* The following four routines (einit, eremove, eput & eget) are parallel */
- /* access queue routines for maintaining the entry queue in tasks' tcbs. */
- /*--------------------------------------------------------------------------*/
-
- static void einit(struct entry_type *entry) /*;einit*/
- {
- ENTRY_LAST(entry) = NULL;
- ENTRY_FIRST(entry) = NULL;
- (LOCK(entry)).add_lock = 0;
- (LOCK(entry)).del_lock = 0;
- ENTRY_GUARD(entry) = 0;
- ENTRY_COUNT(entry) = 0;
- }
-
- static int eremove(int task) /*;eremove*/
- {
- if ((TCB_ENTRY_ITEM(task) == NULL) || (TCB_CURR_ENTRY(task) == NULL))
- return FALSE;
-
- add_lock(&(LOCK(TCB_CURR_ENTRY(task))));
- if (FAS(&(ITEM_FLAG(TCB_ENTRY_ITEM(task))), 0) == 0) {
- add_unlock(&(LOCK(TCB_CURR_ENTRY(task))));
- return FALSE;
- }
- else {
- DEC(ENTRY_COUNT(TCB_CURR_ENTRY(task)));
- add_unlock(&(LOCK(TCB_CURR_ENTRY(task))));
- TCB_CURR_ENTRY(task) = NULL;
- TCB_ENTRY_ITEM(task) = NULL;
- return TRUE;
- }
- }
-
- static void eput(struct entry_type *entry, int caller, struct q_item **ret_item)
- /*;eput*/
- {
- struct q_item *prev_item, *item;
-
- item = (struct q_item*) malloc(sizeof(struct q_item));
- if (item == (struct q_item*)0) {
- raise(STORAGE_ERROR, "Allocating space for entry queue");
- return;
- }
- ITEM_FLAG(item) = 1;
- ITEM_TASK(item) = caller;
- ITEM_NEXT(item) = NULL;
- prev_item = FAS_Q(&(ENTRY_LAST(entry)), item);
- if (prev_item == (struct q_item *)0)
- FAS_Q(&(ENTRY_FIRST(entry)), item);
- else
- FAS_Q(&(ITEM_NEXT(prev_item)), item);
- *ret_item = item;
- }
-
- static int eget(struct entry_type *entry) /*;eget*/
- {
- struct q_item *caller, *new_caller;
- int task;
-
- del_lock(&(LOCK(entry)));
- if (ENTRY_FIRST(entry) == NULL) {
- del_unlock(&(LOCK(entry)));
- return NULL_TASK;
- }
-
- ENTRY_GUARD(entry) = 0;
- caller = FAS_Q(&(ENTRY_FIRST(entry)), (ITEM_NEXT(ENTRY_FIRST(entry))));
- while ((caller != (struct q_item*)0) && (FAS(&(ITEM_FLAG(caller)),0) == 0)){
- new_caller = FAS_Q(&(ENTRY_FIRST(entry)),ITEM_NEXT(ENTRY_FIRST(entry)));
- efree((char *) caller);
- caller = new_caller;
- }
-
- if (caller != (struct q_item*)0) {
- if (TCB_IO_ITEM(ITEM_TASK(caller)) != NULL)
- disable_io(&(TCB_IO_ITEM(ITEM_TASK(caller))));
- if (ENTRY_COUNT(entry) == 0) FAS_Q(&(ENTRY_LAST(entry)), NULL);
- task = ITEM_TASK(caller);
- efree((char *) caller);
- TCB_ENTRY_ITEM(task) = (struct q_item *)0;
- }
- else task = NULL_TASK;
- del_unlock(&(LOCK(entry)));
- return(task);
- }
-
- /*---------------------------------------------------------------------------*
- * LOCKING ROUTINES *
- * *
- * Locks on entries may be either "add" locks or "del" locks. "del" (or *
- * delete) locks have precedence over "add" locks. These are implemented *
- * using a standard A-B locking scheme. *
- *---------------------------------------------------------------------------*/
-
- static void add_lock(struct lock_type *lock) /*;add_lock*/
- {
- for (;;) {
- while (lock->del_lock > 1) continue; /* wait for dels to end */
- INC(lock->add_lock); /* try tp get add lock */
- if (lock->del_lock > 1) /* oops, del started... */
- DEC(lock->add_lock);
- else break;
- }
- }
-
- static void add_unlock(struct lock_type *lock) /*;add_unlock*/
- {
- DEC(lock->add_lock);
- }
-
- static void del_lock(struct lock_type *lock) /*;del_lock */
- {
- INC(lock->del_lock); /* don't allow adds to start */
- while (lock->add_lock > 1) continue; /* wait for adds to end */
- }
-
- static void del_unlock(struct lock_type *lock) /*;del_unlock*/
- {
- DEC(lock->del_lock);
- }
-
- /*-----------------------------------------------------------------------*/
- /* ENTRY NUMBER */
- /*-----------------------------------------------------------------------*/
-
- static int entry_number(int owner, int family, int member) /*;entry_number*/
- {
- return (*ADDR(TCB_TBASE(owner), TCB_TOFF(owner) + 2 * (family -1))
- + member + 1);
- }
-
- /*------------------------------------------------------------------------*
- * RAISE IN CALLER *
- * Procedure to raise an exception in the task waiting for the completion *
- * of the current rendezvous *
- * -----------------------------------------------------------------------*/
-
- void raise_in_caller() /*;raise_in_caller*/
- {
- #ifdef TRACE
- if (exception_trace)
- printf("task %d raising exception %s in task %d\n",tp,
- exception_slots[exr],MY_SERVICED);
- #endif
- TCB_EXCEPTION(MY_SERVICED) = exr;
- }
-
-
- /* -----------------------------------------------------------------------*
- * ATTRIBUTE ROUTINES *
- * The following three routines (is_callable, is_terminated and count) *
- * return the attributes 'CALLABLE, 'TERMINATED and 'COUNT respecitvely *
- * for the given task *
- * -----------------------------------------------------------------------*/
-
- int is_callable(int task) /*;is_callable*/
- {
- if (task != ORIG(task) || STACK(task) == (int *)0 || TCB_ABNORMAL(task))
- return FALSE;
- switch(TCB_STATUS(task)) {
- case TERMINATED:
- case ABNORMAL:
- return FALSE;
- case COMPLETED: /* are we completed on the last level? */
- return BF_PREVIOUS_BFP(task, *(TCB_BLOCK_PTR(task))) == NULL_TASK;
- default:
- return TRUE;
- }
- }
-
- int is_terminated(int task) /*;is_terminated*/
- {
- if (task != ORIG(task) || STACK(task) == (int *)0 ) return TRUE;
- return (TCB_STATUS(task) == TERMINATED);
- }
-
- int count(int family, int member) /*;count*/
- {
- return(ENTRY_COUNT(MY_ENTRY(entry_number(tp, family, member))));
- }
-
- /*---------------------------------------------------------------------------*/
- /* MULTISIGNAL */
- /*---------------------------------------------------------------------------*/
-
- static void multisignal(int task, int event) /*;multisignal*/
- {
- if (event != NO_EVENT) TCB_EVENT(task) = event;
- if (DEC(TCB_NUM_ITEMS(task)) == 1)
- make_ready(task, NO_EVENT);
- }
-
- /*---------------------------------------------------------------------------*/
- /* "SERVICED" QUEUE */
- /*---------------------------------------------------------------------------*/
-
- static void add_serviced(int server, int task) /*;add_serviced*/
- {
- TCB_NEXT(task) = FAS(&(TCB_SERVICED(server)), task);
- }
-
- static int remove_serviced() /*;remove_serviced*/
- {
- return(FAS(&(MY_SERVICED), TCB_NEXT(MY_SERVICED)));
- }
-
- /*--------------------------------------------------------------------------*/
- /* READY QUEUE */
- /* The following three routines (qinit, enqueue, enqueue_item and dequeue) */
- /* are the routines used in accessing the ready queue. The other three */
- /* routines are used to control the scheduler. */
- /*--------------------------------------------------------------------------*/
-
- static void qinit() /*;qinit*/
- {
- int i;
- struct ready_q *q;
-
- for (i = 0; i < MAX_PRIO; i++) {
- ready_queue[i] = (struct ready_q *)malloc(sizeof(struct ready_q));
- q = ready_queue[i];
- if (q == (struct ready_q *)0) {
- #ifdef vms
- LIB$STOP(MSG_NOSTACK);
- #else
- printf("Unable to allocate stack\n");
- exit(RC_ABORT);
- #endif
- }
- q->first = NULL;
- q->last = NULL;
- q->first_mult = 0;
- q->count = 0;
- q->lock.add_lock = 0;
- q->lock.del_lock = 0;
- }
- }
-
- static void enqueue(int priority, int value) /*;enqueue*/
- {
- struct rts_item *item;
-
- item = (struct rts_item *) malloc(sizeof(struct rts_item));
- if (item == (struct rts_item *)0){
- raise(STORAGE_ERROR, "Allocating space for ready queue");
- return;
- }
- item->tcbs = (int *) malloc(sizeof(int));
- if (item->tcbs == (int *)0){
- raise(STORAGE_ERROR, "Allocating space for ready queue");
- return;
- }
- RTS_TYPE(item) = READY;
- RTS_PRIO(item) = priority;
- RTS_MULT(item) = 1;
- RTS_NEXT(item) = NULL;
- RTS_TCBS(item, 0) = value;
- enqueue_item(priority, item);
- }
-
- static void enqueue_item(int priority, struct rts_item *item) /*;enqueue_item*/
- {
- struct ready_q *q;
- struct rts_item *prev;
-
- RTS_SAVE_MULT(item) = RTS_MULT(item);
- q = ready_queue[priority];
- while (highest_priority < priority)
- priority = FAS(&highest_priority, priority);
- add_lock(&(LOCK(q)));
- prev = FAS_RTS(&(q->last), item);
- if (prev == (struct rts_item *)0) {
- FAS_RTS(&(q->first), item);
- FAS(&(q->first_mult), item->mult);
- }
- else FAS_RTS(&(prev->next), item);
- q->count += item->mult;
- add_unlock(&(LOCK(q)));
- }
-
- static struct rts_item *dequeue(struct ready_q *q, int *i) /*;dequeue*/
- {
- struct rts_item *item;
- int j, k;
-
- if (q->count <= 0) return (struct rts_item *)0;
- else if (DEC(q->count) < 1) {
- INC(q->count);
- return (struct rts_item *)0;
- }
- else {
- while (q->first_mult > 0 && DEC(q->first_mult) < 1) continue;
- item = q->first;
- if ((j = DEC(q->first->mult)) == 1) { /* remove from list */
- RTS_MULT(q->first) = RTS_SAVE_MULT(q->first);
- if (q->first->next == NULL) {
- del_lock(&(LOCK(q)));
- if (q->first->next == NULL) {
- FAS_RTS(&q->first, NULL);
- FAS_RTS(&q->last, NULL);
- }
- else {
- FAS_RTS(&(q->first), q->first->next);
- FAS(&(q->first_mult), q->first->mult);
- }
- del_unlock(&(LOCK(q)));
- }
- else {
- FAS_RTS(&(q->first), q->first->next);
- FAS(&(q->first_mult), q->first->mult);
- }
- }
- k = 0;
- *i = -1;
- while (k < j)
- if (item->tcbs[++(*i)] != NULL_TASK) k++;
- return item;
- }
- }
-
- static void context_switch() /*;context_switch*/
- {
- DEC(MY_NUM_EVENTS);
- make_ready(tp, TIMER_EVENT);
- schedule(CONTEXT_SWITCH);
- }
-
- #ifdef IBM_PC
- static void sleep(unsigned secs)
- {
- clock_t check = clock();
- check += secs * CLOCKS_PER_SEC;
- while (clock() < check)
- ;
- }
- #endif
-